#include<iostream>
#include<string>
#include<vector>
#include<fstream>
#include <cerrno>
#include<ctype.h>
#include <cstring>
#include"../source/server.hpp"
using namespace std;

  //字符串分割函数 abc,ada,
  size_t Split(const string& src,const string& sep,vector<string>* array)
  {
      size_t offset = 0;//
      while(offset < src.size())
      {
          size_t pos = src.find(sep,offset); //从src的offset位置开始找sep
          //没找到分隔符：将剩余部分存到数组
          if(pos == std::string::npos)
          {
              array->push_back(src.substr(offset));
              return array->size();
          }
          //abd,,efg对于连续两个分割符不用提取但是注意要跳过分割符
          if(pos == offset)
          {
             offset = pos + sep.size(); //别忘了更新offset
             continue;
          }
          //正常情况
          array->push_back(src.substr(offset,pos-offset));
          offset = pos + sep.size(); //别忘了更新offset
      }
      return array->size();
  }


  void Print(vector<string>& v)
  {
     for(auto& s : v)
       cout << "[" << s << "]" << endl;
    cout << endl;
  }

  bool ReadFile(const string& filename,string* result)
  {
    //1.定义流对象打开文件
    ifstream ifs(filename,std::ios::binary);
    if(!ifs.is_open())
    {
        ERR_LOG("open %s file failed!!",filename.c_str());
        perror("Error");  // 打印系统的错误信息
        ifs.close();
        return false;
    }
    //2.获取文件大小并读取文件(注意使用流提取，遇到换行符会停止)
    string line;
    while (getline(ifs, line)) 
    {
       *result += line;
       *result += "\n";
    }
    // 3. 判断是否读取完文件
    if (ifs.eof())  // 如果是正常的文件结束
    {
        return true;  // 文件读取成功
    }
    // 如果不是因为文件结束而导致读取失败
    ERR_LOG("read %s file failed!!", filename.c_str());
    return false;
  }


    //向文件写入数据
   bool WriteFile(const string& filename,const string& data)
  {
        //1.打开文件
        ofstream ofs(filename,std::ios::binary|std::ios::trunc); 
        if(!ofs.is_open())
        {
            ERR_LOG("open %s file failed!!",filename.c_str());
            ofs.close();
            return false;
        }
        //2.写文件
        ofs << data;
        //3.判断
        if(ofs.good() == false)
        {
            ERR_LOG("write %s file failed!!",filename.c_str());
            ofs.close();
            return false;
        }
        return true;
  }


 //URL编码
  static string UrlEnCode(const string& url,bool is_spaceto_plus)
  {
       string result;
       for(const auto& ch : url)
       {
           //不需要编码
           if(ch == '.' || ch == '-' || ch == '_' || ch == '~' || isalnum(ch))
           {
                result += ch;
                continue;
           }
           //遇到空格
           if(ch == ' ' && is_spaceto_plus)
           {
              result += '+';
              continue;
           }
           //剩下的是需要编码为%HH
           char temp[4];
           snprintf(temp,4,"%%%02X",ch);
           result += temp;
       } 
       return result;
  }

  static char HEXTOB(char c)
  {
      //数字
      if(c >= '0' && c <= '9')
       return c - '0';
      //小写字符
      if(c >= 'a' && c <= 'z')
        return c - 'a' + 10; //注意这里要加10
      if(c >= 'A' && c <= 'Z')
        return c - 'A' + 10;
  }

  static string UrlDeCode(const string& url,bool is_spaceto_plus)
  {
     string result;
     for(int i = 0 ; i < url.size() ; i++)
     {
        char ch = url[i];
        if(ch == '+' && is_spaceto_plus)
        {
              result += ' ';
              continue;
        }
        //遇到%
        if(ch == '%' && i+2 < url.size())
        {
            // cout << (char)url[i+1] << " " << (char)url[i+2] << " " ;
            char ch1 = HEXTOB(url[i+1]);
            char ch2 = HEXTOB(url[i+2]);
            char temp = ch1 * 16 +  ch2;
            // cout << (char)temp << endl;  
            result += (char)temp;
            i += 2; //注意这里要跳过两个字符
            continue;
        }
        //不需要编码
        result += ch;
     }
     return result;
  }


    //响应状态码的描述信息获取
  static string StatuDesc(int statu_code)
  {
     unordered_map<int,string> statu_info = {
        {100,  "Continue"},
        {101,  "Switching Protocol"},
        {102,  "Processing"},
        {103,  "Early Hints"},
        {200,  "OK"},
        {201,  "Created"},
        {202,  "Accepted"},
        {203,  "Non-Authoritative Information"},
        {204,  "No Content"},
        {205,  "Reset Content"},
        {206,  "Partial Content"},
        {207,  "Multi-Status"},
        {208,  "Already Reported"},
        {226,  "IM Used"},
        {300,  "Multiple Choice"},
        {301,  "Moved Permanently"},
        {302,  "Found"},
        {303,  "See Other"},
        {304,  "Not Modified"},
        {305,  "Use Proxy"},
        {306,  "unused"},
        {307,  "Temporary Redirect"},
        {308,  "Permanent Redirect"},
        {400,  "Bad Request"},
        {401,  "Unauthorized"},
        {402,  "Payment Required"},
        {403,  "Forbidden"},
        {404,  "Not Found"},
        {405,  "Method Not Allowed"},
        {406,  "Not Acceptable"},
        {407,  "Proxy Authentication Required"},
        {408,  "Request Timeout"},
        {409,  "Conflict"},
        {410,  "Gone"},
        {411,  "Length Required"},
        {412,  "Precondition Failed"},
        {413,  "Payload Too Large"},
        {414,  "URI Too Long"},
        {415,  "Unsupported Media Type"},
        {416,  "Range Not Satisfiable"},
        {417,  "Expectation Failed"},
        {418,  "I'm a teapot"},
        {421,  "Misdirected Request"},
        {422,  "Unprocessable Entity"},
        {423,  "Locked"},
        {424,  "Failed Dependency"},
        {425,  "Too Early"},
        {426,  "Upgrade Required"},
        {428,  "Precondition Required"},
        {429,  "Too Many Requests"},
        {431,  "Request Header Fields Too Large"},
        {451,  "Unavailable For Legal Reasons"},
        {501,  "Not Implemented"},
        {502,  "Bad Gateway"},
        {503,  "Service Unavailable"},
        {504,  "Gateway Timeout"},
        {505,  "HTTP Version Not Supported"},
        {506,  "Variant Also Negotiates"},
        {507,  "Insufficient Storage"},
        {508,  "Loop Detected"},
        {510,  "Not Extended"},
        {511,  "Network Authentication Required"}
     };
     auto it = statu_info.find(statu_code);
     if(it == statu_info.end())
       return "NotKown Statu";
     return it->second; 
  }
  
  //根据文件后缀名获取文件mime
  static string ExtMime(const string& filename)
  {
      //1.先根据文件名获取文件后缀
      size_t pos = filename.find_last_of('.');
      if(pos == string::npos)
          return "No Suffix";
       string Suffix = filename.substr(pos); 
      //2.哈希表中查找
      unordered_map<string,string> file_mime = {
        {".aac",        "audio/aac"},
        {".abw",        "application/x-abiword"},
        {".arc",        "application/x-freearc"},
        {".avi",        "video/x-msvideo"},
        {".azw",        "application/vnd.amazon.ebook"},
        {".bin",        "application/octet-stream"},
        {".bmp",        "image/bmp"},
        {".bz",         "application/x-bzip"},
        {".bz2",        "application/x-bzip2"},
        {".csh",        "application/x-csh"},
        {".css",        "text/css"},
        {".csv",        "text/csv"},
        {".doc",        "application/msword"},
        {".docx",       "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
        {".eot",        "application/vnd.ms-fontobject"},
        {".epub",       "application/epub+zip"},
        {".gif",        "image/gif"},
        {".htm",        "text/html"},
        {".html",       "text/html"},
        {".ico",        "image/vnd.microsoft.icon"},
        {".ics",        "text/calendar"},
        {".jar",        "application/java-archive"},
        {".jpeg",       "image/jpeg"},
        {".jpg",        "image/jpeg"},
        {".js",         "text/javascript"},
        {".json",       "application/json"},
        {".jsonld",     "application/ld+json"},
        {".mid",        "audio/midi"},
        {".midi",       "audio/x-midi"},
        {".mjs",        "text/javascript"},
        {".mp3",        "audio/mpeg"},
        {".mpeg",       "video/mpeg"},
        {".mpkg",       "application/vnd.apple.installer+xml"},
        {".odp",        "application/vnd.oasis.opendocument.presentation"},
        {".ods",        "application/vnd.oasis.opendocument.spreadsheet"},
        {".odt",        "application/vnd.oasis.opendocument.text"},
        {".oga",        "audio/ogg"},
        {".ogv",        "video/ogg"},
        {".ogx",        "application/ogg"},
        {".otf",        "font/otf"},
        {".png",        "image/png"},
        {".pdf",        "application/pdf"},
        {".ppt",        "application/vnd.ms-powerpoint"},
        {".pptx",       "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
        {".rar",        "application/x-rar-compressed"},
        {".rtf",        "application/rtf"},
        {".sh",         "application/x-sh"},
        {".svg",        "image/svg+xml"},
        {".swf",        "application/x-shockwave-flash"},
        {".tar",        "application/x-tar"},
        {".tif",        "image/tiff"},
        {".tiff",       "image/tiff"},
        {".ttf",        "font/ttf"},
        {".txt",        "text/plain"},
        {".vsd",        "application/vnd.visio"},
        {".wav",        "audio/wav"},
        {".weba",       "audio/webm"},
        {".webm",       "video/webm"},
        {".webp",       "image/webp"},
        {".woff",       "font/woff"},
        {".woff2",      "font/woff2"},
        {".xhtml",      "application/xhtml+xml"},
        {".xls",        "application/vnd.ms-excel"},
        {".xlsx",       "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
        {".xml",        "application/xml"},
        {".xul",        "application/vnd.mozilla.xul+xml"},
        {".zip",        "application/zip"},
        {".3gp",        "video/3gpp"},
        {".3g2",        "video/3gpp2"},
        {".7z",         "application/x-7z-compressed"}
      };
      auto it = file_mime.find(Suffix);
      if(it == file_mime.end())
       return "NotKown Mime";
      return it->second; 

  }

    //判断一个文件是否是一个目录
  static bool isDirectory(const string& filename )
  {
    //1.获取文件属性
    struct stat st;
    int n = ::stat(filename.c_str(),&st);
    if(n < 0) return false;
    //2.s_mode
    return S_ISDIR(st.st_mode);
  }

  //判断一个文件是否是一个普通文件
  static bool isRegular(const string& filename )
  {
    //1.获取文件属性
    struct stat st;
    int n = ::stat(filename.c_str(),&st);
    if(n < 0) return false;
    //2.s_mode
    return S_ISREG(st.st_mode);
  }

  static bool ValidPath(const string& path)
  {
      int level = 0 ;
      //1.字符串切分
      vector<string> array;
      Split(path,"/",&array);
      //2.计算深度
      for(auto& s : array)
      {
         if(s == "..")
         {
            level--;
            if(level < 0) return false;
            continue;
         }
         level++;
      }
      return true;
  }

  int main()
  {

    cout << ValidPath("/../index.html") << endl;
    cout << ValidPath("/http/../index.html") << endl;
    cout << ValidPath("/http/../index/../test.html") << endl;

    // cout << isDirectory("./http.hpp") << endl;
    // cout << isRegular("./http.hpp") << endl;
    // cout << isDirectory("../http") << endl;
    // cout << isRegular("../http") << endl;

    // int statu1 = 226;
    // int statu2 = 999;
    // cout << statu1 << ": " << StatuDesc(statu1) << endl;
    // cout << statu2 << ": " << StatuDesc(statu2) << endl;
    // string file1 = "file1.bmp";
    // string file2 = "file.zp";
    // cout << file1 << " : " << ExtMime(file1) << endl;
    // cout << file2 << " : " << ExtMime(file2) << endl;

    // string s1 = UrlEnCode("https://sass-node2.chatshare.biz/c",false);
    // string s2 = UrlEnCode("https://sass-node2.chatshare.biz/c sda",true);
    // string s3 = UrlEnCode("https://sass-node2.chatshare.biz/c\t",true);
    // cout << s1 << endl;
    // cout << s2 << endl;
    // cout << s3 << endl;
    // cout << "------------------------------------------------" << endl;
    // s1 = UrlDeCode(s1,false);
    // s2 = UrlDeCode(s2,true);
    // s3 = UrlDeCode(s3,true);
    // cout << s1 << endl;
    // cout << s2 << endl;
    // cout << s3 << endl;

    //  string src1 = "abc,bcd,efg";
    //  string src2 = "abc,bcd,efg,";
    //  string src3 = "abc,,,,bcd,efg,";
    //  vector<string> a1;
    //  vector<string> a2;
    //  vector<string> a3;
    //  Split(src1,",",&a1);
    //  Split(src2,",",&a2);
    //  Split(src3,",",&a3);
    //  Print(a1);
    //  Print(a2);
    //  Print(a3);
    //  bool ret =  ReadFile("./Util.hpp",&s);
    //  cout << s  << endl ;
    //  cout << ret << endl;
    //  WriteFile("log.txt",s);
     return 0;
  }
